import { IRenderPass } from "./render-pass"
import { WebGpuRendererContext } from "./webgpu-renderer"

export type StageKey = number

export interface InPipelineResource {
    dispose()
}

export class InPipelineRenderTexture implements InPipelineResource {
    protected gpuTexture: GPUTexture
    protected disposed: boolean = false
    protected disposable: boolean = true

    protected constructor() {}

    dispose() {
        if (!this.disposable || this.disposed)
            return

        this.disposed = true
        this.gpuTexture.destroy()
    }
}

class InternalInPipelineRenderTexture extends InPipelineRenderTexture {
    constructor(gpuTexture: GPUTexture, disposable: boolean = true) {
        super()

        this.gpuTexture = gpuTexture
        this.disposable = disposable
    }

    getRawGpuTexture() {
        return this.gpuTexture
    }
}

class RenderStagePassGroup {
    passes: IRenderPass[]
}

export interface PipelineContext {
    getTemporaryRenderTexture(descriptor: GPUTextureDescriptor): InPipelineRenderTexture
    getCurrentRenderTarget(): InPipelineRenderTexture
}

export class WebGpuProgrammableRenderPipeline {
    protected stageDict: Record<StageKey, RenderStagePassGroup> = {}
    protected stageOrder: Array<StageKey> = []
    protected bakedPassList: Array<IRenderPass> = []

    protected rendererContext: WebGpuRendererContext
    protected pipelineContext: PipelineContext

    protected passTemporaryTextureList: Array<InternalInPipelineRenderTexture> = []

    protected currentRenderPassEncoder: GPURenderPassEncoder

    constructor(rendererContext: WebGpuRendererContext) {
        this.rendererContext = rendererContext
        
        this.pipelineContext = {
            getTemporaryRenderTexture: 
                (descriptor: GPUTextureDescriptor) => this.requestAndManageTemporyRenderTexutre(descriptor),
            getCurrentRenderTarget: () => this.getCurrentRenderTargetTexture()   
        }
    }

    protected bakeStageList() {
        const list = []

        for (const key of this.stageOrder) {
            const stage = this.stageDict[key]
            if (stage) {
                for (const pass of stage.passes) {
                    list.push(pass)
                }
            }
        }

        this.bakedPassList = list
    }

    protected requestAndManageTemporyRenderTexutre(descriptor: GPUTextureDescriptor)
            : InternalInPipelineRenderTexture {
        const device = this.rendererContext.getWebGpuDevice()
        const texRef = device.createTexture(descriptor)

        const inPassTex = new InternalInPipelineRenderTexture(texRef)
        this.passTemporaryTextureList.push(inPassTex)

        return inPassTex
    }

    protected getCurrentRenderTargetTexture() {
        return new InternalInPipelineRenderTexture(
            this.rendererContext.getCurrentRenderTargetTexture(), false)
    }

    public setStageExecuteOrder(orderList: Array<StageKey>): WebGpuProgrammableRenderPipeline {
        this.stageOrder = orderList
        this.bakeStageList()

        return this
    }

    public addPass(pass: IRenderPass, stageKey: StageKey): WebGpuProgrammableRenderPipeline {
        if (!this.stageDict[stageKey]) {
            this.stageDict[stageKey] = new RenderStagePassGroup()
        }

        this.stageDict[stageKey].passes.push(pass)

        return this
    }

    protected executeOnePass(pass: IRenderPass, encoder: GPUCommandEncoder) {
        
    }

    public execute() {
        if (this.stageOrder.length !== this.bakedPassList.length) {
            this.bakeStageList()
        }

        const device = this.rendererContext.getWebGpuDevice()
        const commandEncoder = device.createCommandEncoder()

        for (const pass of this.bakedPassList) {
            this.executeOnePass(pass, commandEncoder)
        }
    }
} 
